import java.util.Date;
import java.util.StringTokenizer;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import processing.core.*;
/**
 * This class is a modified version of the Table class that was used in tutorial and in my project 1.
 * It expects the imported file to be of a .csv format.
 * 
 * @author Kevin Wong
 *
 */
public class Table extends PApplet {
	private int rowCount;
	private int columnCount;
	private int COLUMN_NAMES = 5;

	private Object[][]	data;

	private String[]	usageType;
	private Date[]		date;
	//private Date[]		time;
	private String[] 	numberCalled;
	private Integer[] 		duration;
	private Float[] 	dataVolume;
	private String[] 	eventDescription;
	private String[]	category;
	private String[] 	fromColumn;
	private String[] 	toColumn;
	private String[] 	callType;
	private String[] 	eventType;
	private String[] 	details;
	private String[]	billedBy;
	private String[]	longDistanceType;
	private String[]	rate;
	private Float[]		longDistance;
	private	Float[]		airtime;
	private Float[]		total;
	
	private String[]	columnNames;

	public Table(String input) throws ParseException {
		String[] rows = loadStrings(input);
		String[] columns = split(rows[COLUMN_NAMES], ",");
		
		columnNames = subset(columns, 0); // subset extracts array of elements
		columnCount = columnNames.length;

		usageType 			= new String[rows.length - 1]; // these are the values of the first column
		date 				= new Date[rows.length - 1];
		//time				= new Date[rows.length - 1];
		numberCalled 		= new String[rows.length - 1];
		duration			= new Integer[rows.length -1];
		dataVolume			= new Float[rows.length - 1];
		eventDescription 	= new String[rows.length - 1];
		category			= new String[rows.length - 1];
		fromColumn 			= new String[rows.length - 1];
		toColumn 			= new String[rows.length - 1];
		callType 			= new String[rows.length - 1];
		eventType 			= new String[rows.length - 1];
		details 			= new String[rows.length - 1];
		billedBy			= new String[rows.length - 1];
		longDistanceType	= new String[rows.length - 1];
		rate				= new String[rows.length - 1];
		longDistance		= new Float[rows.length - 1];
		airtime				= new Float[rows.length - 1];
		total				= new Float[rows.length - 1];
		
		
		data = new Object[rows.length - 1][];
		
		SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm a");

		// loops through all the rows and split the rows into the right spot in
		// the array; initial row data starts at 6
		for (int i = 6; i < rows.length; i++) {
			String[] piece = split(rows[i], ",");

			usageType[rowCount] 		= piece[0];
			date[rowCount]				= dateFormat.parse(piece[1] + " " + piece[2]);
			numberCalled[rowCount] 		= piece[3];
			duration[rowCount]			= convertTime(piece[4]);			/* duration is stored in minutes */
			//dataVolume[rowCount] 		= Float.parseFloat(piece[5]);
			eventDescription[rowCount] 	= piece[6];
			//category					= piece[7];
			fromColumn[rowCount] 		= piece[8];
			toColumn[rowCount] 			= piece[9];
			callType[rowCount] 			= piece[9];
			eventType[rowCount]			= piece[10];
			details[rowCount] 			= piece[11];
			billedBy[rowCount]			= piece[12];
			longDistanceType[rowCount]	= piece[13];
			rate[rowCount]				= piece[14];
			//longDistance[rowCount]		= Float.parseFloat(piece[15]);
			//airtime[rowCount]			= Float.parseFloat(piece[16]);
			//total[rowCount]				= Float.parseFloat(piece[17]);
			
			try {
				dataVolume[rowCount] = Float.parseFloat(piece[5]);

			} catch (NumberFormatException e) {
				dataVolume[rowCount] = null;
			}
			
			try {
				longDistance[rowCount] = Float.parseFloat(piece[15]);
			} catch (NumberFormatException e) {
				longDistance[rowCount] = null;			
			}
			
			try {
				airtime[rowCount] = Float.parseFloat(piece[16]);
			} catch (NumberFormatException e) {
				airtime[rowCount] = null;			
			}
			
			try {
				total[rowCount]	= Float.parseFloat(piece[17]);
			} catch (NumberFormatException e) {
				total[rowCount]	= null;			
			}
			
			Object[] insertData = {usageType[rowCount], date[rowCount], numberCalled[rowCount], duration[rowCount], dataVolume[rowCount],
					eventDescription[rowCount], fromColumn[rowCount], toColumn[rowCount], callType[rowCount], eventType[rowCount], details[rowCount],
					billedBy[rowCount], longDistanceType[rowCount], rate[rowCount], longDistance[rowCount], airtime[rowCount], total[rowCount]};
			
			data[rowCount] = insertData; //parseFloat(subset(piece, 1)); // the rest of the row
			rowCount++;
		}

		// resizing the data array as necessary
		data = (Object[][]) subset(data, 0, rowCount);
	}
	
	/**
	 * Convert an hh:mm:ss or hh:mm string into a integer duration value in minutes
	 * @param time	hh:mm:ss or hh:mm format
	 * @return		Integer duration value in minutes
	 */
	private Integer convertTime(String time) {
		int hours;
		int minutes;
		int seconds;	// never used cause Bell doesn't count seconds

		StringTokenizer st = new StringTokenizer(time, ":");
		
		hours = Integer.parseInt(st.nextToken());
		minutes = Integer.parseInt(st.nextToken());
		if (st.hasMoreTokens())
			seconds = Integer.parseInt(st.nextToken());
		
		return (hours * 60) + minutes;
		
	}

	int getRowCount() {
		return rowCount;
	}

	String getRowName(int rowIndex) {
		return usageType[rowIndex];
	}
	
	Object[] getRow(int rowIndex) {
		return data[rowIndex];
	}

	int getColumnCount() {
		return columnCount;
	}

	String getColumnName(int colIndex) {
		return columnNames[colIndex];
	}

	String[] getColumnNames() {
		return columnNames;
	}

	Object getValue(int rowIndex, int col) {
		return data[rowIndex][col];
	}
	
	String getStringValue(int rowIndex, int col) {
		return data[rowIndex][col].toString();
	}
	
	Date getDate(int rowIndex) {
		return (Date) data[rowIndex][1];
	}
	
	Float getData(int rowIndex) {
		return dataVolume[rowIndex];
	}
	
	/**
	 * Retrieves the usage type of the given row
	 * @param rowIndex
	 * @return
	 */
	String getUsageType(int rowIndex) {
		return (String) data[rowIndex][0];
	}
	
	/**
	 * Retrieves the duration for a given row
	 * @param rowIndex	index of the row
	 * @return			duration of the transaction
	 */
	Integer getDuration(int rowIndex) {
		return  (Integer) data[rowIndex][3];
	}
	
	Integer getMaxDuration() {
		Integer maxVoiceDuration = 0;
		
		for (int i = 0; i < rowCount; i++) {
			if (usageType[i].equals("Voice")) {
				if (duration[i].intValue() > maxVoiceDuration.intValue())
					maxVoiceDuration = duration[i]; 
			}
		}
		return maxVoiceDuration;
	}
	
	Float getMaxDataVolume() {
		float maxDataVolume = 0;
		
		for (int i = 0; i < rowCount; i++) {
			if (usageType[i].equals("Data")) {
				if (dataVolume[i] > maxDataVolume)
					maxDataVolume = dataVolume[i]; 
			}
		}
		return maxDataVolume;
	}
	
	/**
	 * Retrieves the total used minutes 
	 * @return
	 * @throws ParseException 
	 * @throws ParseException 
	 */
	Integer getTotalChargableVoiceDuration() throws ParseException {
		int voiceDuration = 0;
		
		for (int i = 0; i < rowCount; i++) {
			int dayOfTheWeek = date[i].getDay();
			int time = date[i].getHours();
			
			if (usageType[i].equals("Voice") && 
					(callType[i].contains("OUTGOING") || callType[i].contains("INCOMING")) && 
					(dayOfTheWeek == 1 || dayOfTheWeek == 2 || dayOfTheWeek == 3 || dayOfTheWeek == 4 || dayOfTheWeek == 5 )  &&
					((time >= 7) && (time < 18)) ) {
				voiceDuration += duration[i];
			}
		}
		return voiceDuration;
	}
	
	/**
	 * Retrieves the total amount of minutes used for the month for sending and receiving
	 * @return		the total amount of minutes used
	 */
	Integer getTotalDurationVoice() {
		int voiceDuration = 0;
		
		for (int i = 0; i < rowCount; i++) {
			if (usageType[i].equals("Voice")) {
				voiceDuration += duration[i]; 
			}
		}
		return voiceDuration;
	}
	
	/**
	 * Retrieves the amount of minutes used for numbers that are part of a fab
	 * @return		the amount of minutes used
	 */
	Integer getTotalDurationFabVoice() {
		int voiceDuration = 0;
		
		for (int i = 0; i < rowCount; i++) {
			if (usageType[i].contains("Voice") && callType[i].contains("Fab"))
				voiceDuration += duration[i];
		}
		return voiceDuration;
	}
	
	/**
	 * Retrieves the amount of data used for a given day in megabytes
	 * @param day	the day of the month
	 * @return		the amount of data used in megabytes
	 */
	@SuppressWarnings("deprecation")
	Float getDataForGivenDay(int day) {
		float dataUsage = 0;
		
		for (int i = 0; i < rowCount; i++) {
			int currentDay = date[i].getDate();
			if (usageType[i].contains("Data") && day == currentDay) {
				dataUsage += duration[i];
			}
		}
		return dataUsage;
	}
	
	/**
	 * Retrieves the total amount of data used for the month in megabytes
	 * @return		the amount of data used in megabytes
	 */
	Float getDataTotalUsage() {
		float dataUsage = 0;
		
		for (int i = 0; i < rowCount; i++) {
			if (usageType[i].contains("Data")) 
				dataUsage += dataVolume[i];
		}
		return dataUsage;
	}
	
	Integer getAverageTextPerDay() {
		int textMsgs = 0;
		
		for (int i = 0; i < rowCount; i++) {
			if (usageType[i].contains("Text messages")) {
				textMsgs += duration[i];
			}
		}
		return textMsgs/31;
	}
	
	/**
	 * Retrieves the total amount of text messages sent and received for the month
	 * @return		total text messages sent and received
	 */
	Integer getTextTotalMsgs() {
		int textMsgs = 0;
		
		for (int i = 0; i < rowCount; i++) {
			if (usageType[i].contains("Text messages"))
				textMsgs += duration[i];
		}
		return textMsgs;
	}

}